import type { ExtractPropTypes } from 'vue'
import type { TreeDataItem } from 'ant-design-vue/es/tree/Tree'

import { buildProps } from '@/utils/props'

export enum ToolbarEnum {
  SELECT_ALL,
  UN_SELECT_ALL,
  EXPAND_ALL,
  UN_EXPAND_ALL,
  CHECK_STRICTLY,
  CHECK_UN_STRICTLY
}

export const treeEmits = ['update:expandedKeys', 'update:selectedKeys', 'update:value', 'change', 'check', 'update:searchValue']

export interface TreeState {
  expandedKeys: KeyType[]
  selectedKeys: KeyType[]
  checkedKeys: CheckKeys
  checkStrictly: boolean
}

export interface FieldNames {
  children?: string
  title?: string
  key?: string
}

export type KeyType = string | number

export type CheckKeys = KeyType[] | { checked: string[] | number[]; halfChecked: string[] | number[] }

export const treeProps = buildProps({
  value: {
    type: [Object, Array] as PropType<KeyType[] | CheckKeys>
  },

  renderIcon: {
    type: Function as PropType<(params: Recordable) => string>
  },

  helpMessage: {
    type: [String, Array] as PropType<string | string[]>,
    default: ''
  },

  title: {
    type: String,
    default: ''
  },
  toolbar: Boolean,
  search: Boolean,
  searchValue: {
    type: String,
    default: ''
  },
  checkStrictly: Boolean,
  clickRowToExpand: {
    type: Boolean,
    default: false
  },
  checkable: Boolean,
  defaultExpandLevel: {
    type: [String, Number] as PropType<string | number>,
    default: ''
  },
  defaultExpandAll: Boolean,

  fieldNames: {
    type: Object as PropType<FieldNames>
  },

  treeData: {
    type: Array as PropType<TreeDataItem[]>
  },

  actionList: {
    type: Array as PropType<TreeActionItem[]>,
    default: () => []
  },

  expandedKeys: {
    type: Array as PropType<KeyType[]>,
    default: () => []
  },

  selectedKeys: {
    type: Array as PropType<KeyType[]>,
    default: () => []
  },

  checkedKeys: {
    type: [Array, Object] as PropType<CheckKeys>,
    default: () => []
  },

  beforeRightClick: {
    type: Function as PropType<(...arg: any) => ContextMenuItem[] | ContextMenuOptions>,
    default: undefined
  },

  rightMenuList: {
    type: Array as PropType<ContextMenuItem[]>
  },
  // 自定义数据过滤判断方法(注: 不是整个过滤方法，而是内置过滤的判断方法，用于增强原本仅能通过title进行过滤的方式)
  filterFn: {
    type: Function as PropType<(searchValue: any, node: TreeItem, fieldNames: FieldNames) => boolean>,
    default: undefined
  },
  // 高亮搜索值，仅高亮具体匹配值（通过title）值为true时使用默认色值，值为#xxx时使用此值替代且高亮开启
  highlight: {
    type: [Boolean, String] as PropType<Boolean | String>,
    default: false
  },
  // 搜索完成时自动展开结果
  expandOnSearch: Boolean,
  // 搜索完成自动选中所有结果,当且仅当 checkable===true 时生效
  checkOnSearch: Boolean,
  // 搜索完成自动select所有结果
  selectedOnSearch: Boolean,
  loading: {
    type: Boolean,
    default: false
  },
  treeWrapperClassName: String
})

export type TreeProps = ExtractPropTypes<typeof treeProps>

export interface ContextMenuItem {
  label: string
  icon?: string
  hidden?: boolean
  disabled?: boolean
  handler?: Fn
  divider?: boolean
  children?: ContextMenuItem[]
}

export interface ContextMenuOptions {
  icon?: string
  styles?: any
  items?: ContextMenuItem[]
}

export interface TreeItem extends TreeDataItem {
  icon?: any
}

export interface TreeActionItem {
  render: (record: Recordable) => any
  show?: boolean | ((record: Recordable) => boolean)
}

export interface InsertNodeParams {
  parentKey: string | null
  node: TreeDataItem
  list?: TreeDataItem[]
  push?: 'push' | 'unshift'
}

export interface TreeActionType {
  checkAll: (checkAll: boolean) => void
  expandAll: (expandAll: boolean) => void
  setExpandedKeys: (keys: KeyType[]) => void
  getExpandedKeys: () => KeyType[]
  setSelectedKeys: (keys: KeyType[]) => void
  getSelectedKeys: () => KeyType[]
  setCheckedKeys: (keys: CheckKeys) => void
  getCheckedKeys: () => CheckKeys
  filterByLevel: (level: number) => void
  insertNodeByKey: (opt: InsertNodeParams) => void
  insertNodesByKey: (opt: InsertNodeParams) => void
  deleteNodeByKey: (key: string) => void
  updateNodeByKey: (key: string, node: Omit<TreeDataItem, 'key'>) => void
  setSearchValue: (value: string) => void
  getSearchValue: () => string
  getSelectedNode: (key: KeyType, treeList?: TreeItem[], selectNode?: TreeItem | null) => TreeItem | null
}
